package com.atguigu.gmall.wms.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.exception.OrderException;
import com.atguigu.gmall.wms.vo.SkuLockVo;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.gmall.common.bean.PageResultVo;
import com.atguigu.gmall.common.bean.PageParamVo;

import com.atguigu.gmall.wms.mapper.WareSkuMapper;
import com.atguigu.gmall.wms.entity.WareSkuEntity;
import com.atguigu.gmall.wms.service.WareSkuService;
import org.springframework.util.CollectionUtils;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuMapper, WareSkuEntity> implements WareSkuService {

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private WareSkuMapper wareSkuMapper;

    private static final String LOCK_PREFIX = "stock:lock:"; //分布式锁
    private static final String KEY_PREFIX = "stock:info:"; // Redis中存储锁住的仓库信息及库存信息

    @Override
    public PageResultVo queryPage(PageParamVo paramVo) {
        IPage<WareSkuEntity> page = this.page(
                paramVo.getPage(),
                new QueryWrapper<WareSkuEntity>()
        );

        return new PageResultVo(page);
    }

    @Override
    public List<SkuLockVo> checkLock(List<SkuLockVo> lockVos, String orderToken) {
        // 判断
        if (CollectionUtils.isEmpty(lockVos)){
            throw new OrderException("不存在要购买的商品");
        }
        // 注意 不是所有商品一块上锁解锁  遍历每一个商品将其上锁或者解锁 以一个单体的视角
        // 1 遍历所有商品 检验每一个商品的库存并锁库存
        lockVos.forEach(lockVo -> {
            this.checkAndLock(lockVo);
        });

        // 3 判断是否存在锁定失败的商品, 如果存在则把锁定成功的库存一并解锁
        if (lockVos.stream().anyMatch(lockVo -> !lockVo.getLock())){
            lockVos.stream().filter(SkuLockVo::getLock).collect(Collectors.toList()).forEach(lockVo -> {
                // 解锁当前商品  库存id在下面方法上锁是已经设置了,所以一定存在
                this.wareSkuMapper.unlock(lockVo.getWareSkuId(), lockVo.getCount());
            });
            // 6-1 如果有锁定失败的向页面返回所有商品的具体锁定信息
            return lockVos;
        }
        //  4 所有商品锁定成功的情况下缓存锁定信息
        // 为了方便将来用户取消订单等情况需要解锁库存 或者支付订单减库存操作 将商品锁库存的信息保存到Redis中
        this.redisTemplate.opsForValue().set(KEY_PREFIX+orderToken, JSON.toJSONString(lockVos));

        // 5 TODO 锁定成功发送消息定时解锁库存

        // 6-2 所有商品锁定成功,返回null
        return null;
    }

    private void checkAndLock(SkuLockVo lockVo){
        // 分布式锁
        RLock lock = this.redissonClient.getLock(LOCK_PREFIX + lockVo.getSkuId());
        lock.lock();

        try {
            // 2-1 验库存  本质是查询
            List<WareSkuEntity> wareSkuEntities = this.wareSkuMapper.check(lockVo.getSkuId(), lockVo.getCount());
            if (CollectionUtils.isEmpty(wareSkuEntities)){
                // 该商品库存不足 锁定失败
                lockVo.setLock(false);
                return;
            }
            // 2-2 锁库存  获取第一个仓库的信息   本质是更新
            WareSkuEntity wareSkuEntity = wareSkuEntities.get(0);
            if (this.wareSkuMapper.lock(wareSkuEntity.getId(), lockVo.getCount())==1) {
                // 如果锁库存方法返回1 说明确实锁库存成功
                lockVo.setLock(true);
                // 锁定成功的情况下,记录锁定成功的仓库id,以方便将来解锁
                lockVo.setWareSkuId(wareSkuEntity.getId());
            }else {
                lockVo.setLock(false);
            }
        } finally {
            lock.unlock();
        }
    }


}